#define _GNU_SOURCE
#include <fcntl.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>

#include <netinet/in.h>

#include <zlib.h>

//input, raw little endian ARGB, 8 bits per color
//output, hard coded to png RGBA pixel, 8 bits per color
//usage: rawargb2png input_file output_file width height

static uint8_t png_sig[]={0x89,0x50,0x4e,0x47,0x0d,0x0a,0x1a,0x0a};

static struct {
  uint32_t data_length;
  uint8_t type[4];
  struct {
    uint32_t w;
    uint32_t h;
    uint8_t bit_depth;
    uint8_t color_type;
    uint8_t compression_method;
    uint8_t filter_method;
    uint8_t interlace_method;
  } __attribute__ ((packed)) data;
  uint32_t crc;
} __attribute__ ((packed)) chunk_ihdr={
  .type[0]='I',.type[1]='H',.type[2]='D',.type[3]='R',.data.bit_depth=8,
  .data.color_type=6};

static uint8_t chunk_iend[]={0,0,0,0,'I','E','N','D',0xae,0x42,0x60,0x82};

int main(int argc,char **argv)
{
  uint32_t w=strtoul(argv[3],NULL,10);
  uint32_t h=strtoul(argv[4],NULL,10);

  //load the raw buffer
  int sf=open(argv[1],O_RDONLY);
  unsigned sb_sz=(w*4)*h;
  void *sb=malloc(sb_sz);

  size_t bs_left=sb_sz;
  do{//atomic read
    ssize_t bs_read=read(sf,sb+(sb_sz-bs_left),bs_left);
    if(bs_read<0){
      if(errno!=EINTR) return EXIT_FAILURE;
      bs_read=0;
    }
    bs_left-=bs_read;
  }while(bs_left!=0);

  //build the filtered buffer
  unsigned filtered_sb_sz=(w*4+1)*h;
  void *filtered_sb=malloc(filtered_sb_sz);
  for(unsigned l=0;l<h;++l){
	uint8_t *filter=filtered_sb+(l*(w*4+1));
    *filter=0;//byte at the beginning of each scanline

	for(unsigned c=0;c<w;++c){//endian&alpha overide
      uint8_t *spix=(void*)sb+(l*(w*4)+c*4);
      uint8_t *dpix=(void*)filter+(1+c*4);
      dpix[0]=spix[2];
      dpix[1]=spix[1];
      dpix[2]=spix[0];
      dpix[3]=0xff;
    }
  }

  //compress the filtered buffer
  unsigned long csb_sz=compressBound(filtered_sb_sz);
  void *csb=malloc(csb_sz);
  compress(csb,&csb_sz,filtered_sb,filtered_sb_sz);

  //write destination
  int df=creat(argv[2],S_IRUSR|S_IWUSR);
  write(df,png_sig,sizeof(png_sig));

  //IHDR
  chunk_ihdr.data_length=htonl(sizeof(chunk_ihdr.data));
  chunk_ihdr.data.w=htonl(w);
  chunk_ihdr.data.h=htonl(h);
  chunk_ihdr.crc=crc32(0,Z_NULL,0);
  chunk_ihdr.crc=htonl(crc32(chunk_ihdr.crc,chunk_ihdr.type,
                              sizeof(chunk_ihdr.type)+sizeof(chunk_ihdr.data)));
  write(df,&chunk_ihdr,sizeof(chunk_ihdr));

  //IDAT
  uint32_t chunk_idat_data_length=htonl(csb_sz);
  write(df,&chunk_idat_data_length,sizeof(chunk_idat_data_length));
  uint32_t chunk_idat_crc=crc32(0,Z_NULL,0);
  uint8_t chunk_idat_type[4]={'I','D','A','T'};
  write(df,chunk_idat_type,sizeof(chunk_idat_type));
  chunk_idat_crc=crc32(chunk_idat_crc,chunk_idat_type,sizeof(chunk_idat_type));
  write(df,csb,csb_sz);
  chunk_idat_crc=crc32(chunk_idat_crc,csb,csb_sz);
  chunk_idat_crc=htonl(chunk_idat_crc);
  write(df,&chunk_idat_crc,sizeof(chunk_idat_crc));

  //IEND
  write(df,chunk_iend,sizeof(chunk_iend));
  return 0;
}
